package org.yzh.web.waterMeter.codec;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.CompositeByteBuf;
import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.channel.ChannelHandlerContext;
import org.yzh.framework.codec.MessageDecoder;
import org.yzh.framework.commons.TerminalConnectException;
import org.yzh.framework.commons.transform.Bcd;
import org.yzh.framework.commons.transform.ByteBufUtils;
import org.yzh.framework.mapping.Handler;
import org.yzh.framework.mapping.HandlerMapper;
import org.yzh.framework.message.AbstractBody;
import org.yzh.framework.message.AbstractMessage;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import java.lang.reflect.Type;
import java.nio.ByteOrder;
import java.util.List;


/**
 * 808协议解码器
 *
 * @author zhihao.ye (yezhihaoo@gmail.com)
 */
public class WaterMeterMessageDecoder extends MessageDecoder {

    private final int maxFrameLength = 255;
    private final int headLength = 6;
    private final int endLength = 2;
    private final int LessLength = 20;


    public WaterMeterMessageDecoder() {
    }

    public WaterMeterMessageDecoder(HandlerMapper handlerMapper) {
        super(handlerMapper);
    }


    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
        System.out.println("--------------:" + ByteBufUtil.hexDump(in)+ "-------------");
        ByteBuf buf = cutByte(in);
        if(buf == null){
            return;
        }
        super.decode(ctx,buf,out);
    }

    /**
     * 断包处理
     */
    public ByteBuf cutByte(ByteBuf in) {

        //重置开始位
        resetStartIndex(in);

        if (in.readableBytes() < LessLength) {
            //in.resetReaderIndex();
            return null;
        }

        //检验报文长度
        int frameLength = this.getLength(in);
//        if(frameLength + headLength + endLength > maxFrameLength ){
//            in.skipBytes(1);
//            return null;
//            //throw new TerminalConnectException("报文长度超过规定长度");
//        }

        //长度大于可读数据，则不读，等待下一个报文
        if(frameLength + headLength + endLength > in.readableBytes()){
            in.skipBytes(1);
            return null;
        }

        //检验报文结尾是否是0x16，若不正确，整证明报文开始位不正确，等待下一个报文再读取，in往下移位
        if(!checkEnd(in, frameLength)){
            in.skipBytes(1);
            return null;
            //throw new TerminalConnectException("报文结束位不正确");
        }

        ByteBuf frame = in.retainedSlice(in.readerIndex(), frameLength + headLength + endLength );
        in.skipBytes(frameLength + headLength + endLength);
        return frame;

    }

    /**
     * 重置开始位
     * 重置开始为，符合 0x68 0x** 0x?? 0x** 0x?? 0x68
     * 0x** 0x?? = 0x** 0x??
     * @param in
     */
    public void resetStartIndex(ByteBuf in) {
        if(in.readableBytes() < LessLength){
            return;
        }
        int readerIndex = in.readerIndex();
        int startIndex = in.indexOf(readerIndex,in.writerIndex(), (byte) 0x68);
        int startIndexLast = in.indexOf(startIndex + 5,in.writerIndex(), (byte) 0x68);
        //如果两个0x68不是相隔四位，或者长度不一致，则从下一个0x68开始搜索
        if(startIndexLast - startIndex != 5 || in.getUnsignedShort(1 + startIndex) != in.getUnsignedShort(3 + startIndex )){
            int nextStartIndex = in.indexOf(startIndex + 1,in.writerIndex(), (byte) 0x68);
            if(nextStartIndex < 0){//已经找不到0x68
                in.skipBytes(in.readableBytes());
                System.out.println("报文有误，无开始节点");
                //throw new TerminalConnectException("报文有误，无开始节点");
            }else{
                in.skipBytes(nextStartIndex - readerIndex);
                resetStartIndex(in);
            }
        }else{//直接跳到下一个节点读数
            in.skipBytes(startIndex - readerIndex);
        }
    }

    /**
     * 获取消息长度
     */
    public int getLength(ByteBuf in) {
        int readerIndex = in.readerIndex();
        int length = (in.getUnsignedShort(readerIndex + 2)) >>> 2;
        return length;
    }

    /**
     * 获取消息长度
     */
    public boolean checkEnd(ByteBuf in, int frameLength) {
        int readerIndex = in.readerIndex();
        if(0x16 == in.getByte(readerIndex + headLength + frameLength + 1)){
            return true;
        }else{
            return false;
        }
    }

    /**
     * 获取消息类型
     */
    @Override
    public int getType(ByteBuf source) {
        return source.getUnsignedShort(16);
    }

    /**
     * 校验
     */
    @Override
    public boolean check(ByteBuf buf) {

        ByteBuf checkBuf = buf.slice(6,buf.writerIndex()-6);
        byte checkCode = ByteBufUtils.sum(checkBuf);
        if(checkCode == buf.getByte(buf.readableBytes() - 2)){
            return true;
        }

        return false;
    }

    /**
     * 反转义,重置报文成可解析性报文
     */
    @Override
    public ByteBuf unEscape(ByteBuf source) {

        //提取报文长度进行互换
        ByteBuf lengthBuf = ByteBufUtils.reverseByteBuf(ByteBufUtils.getLengthByteBuf(source));

        //修正长度 + 31
        this.correctLengthByteBuf(source);

        //提取终端号进行互换
        ByteBuf terminalInfoBuf = ByteBufUtils.reverseByteBuf(ByteBufUtils.getTerminalInfoByteBuf(source));

        //提取用户报文进行转换
        ByteBuf userInfoBuf = ByteBufUtils.reverseByteBuf(ByteBufUtils.getUserInfoByteBuf(source));

        return source;


    }

    /**
     * 修改报文长度
     * @param source 源报文
     * @return
     */
    public void correctLengthByteBuf(ByteBuf source){
        int low = source.readerIndex();
        int length = source.getUnsignedShort(low+1);
        length = (length >>> 2) - 12;
        source.setByte(low+1,length >>> 8);
        source.setByte(low+2,length);
        source.setByte(low+3,length >>> 8);
        source.setByte(low+4,length);
    }


}